﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GraphicSplit101
{
    /// <summary>
    /// 样本学习
    /// </summary>
    public partial class UCSampleLearning : UserControl
    {
        private int pid = 1;

        public int Pid
        {
            get { return pid; }
            set { pid = value; }
        }

        public UCSampleLearning()
        {
            InitializeComponent();
        }

        private void btnSampleLearning_Click(object sender, EventArgs e)
        {
            try
            {
                btnSampleLearning.Enabled = false;

                //检测区域
                DataTable dtInspection = DBUtils.GetDataTable("select top 1 [top],[left],width,height from InspectionAreaDetail where [type]=2 and pid='" + Pid + "'");
                //忽略区域
                DataTable dtInspectionIgnore = DBUtils.GetDataTable("select [top],[left],width,height from InspectionAreaDetail where [type]=3 and pid='" + Pid + "'");
                Bitmap bitmap = GraphicSplit101.Properties.Resources.Sample;
                byte[,] grayBytes = GetGrayBytes2D(bitmap);
                IgnoreBytes(grayBytes, dtInspectionIgnore);
                byte[,] availableBytes;
                if (dtInspection.Rows.Count > 0)
                {
                    DataRow dataRow = dtInspection.Rows[0];
                    Rectangle rectangle = new Rectangle(Convert.ToInt32(dataRow["left"]), Convert.ToInt32(dataRow["top"]),
                        Convert.ToInt32(dataRow["width"]), Convert.ToInt32(dataRow["height"]));
                    availableBytes = GetAvailableBytes2D(grayBytes, rectangle);
                }
                else
                {
                    availableBytes = grayBytes;
                }
                ucGraphicSplit1.AvailableBytes = availableBytes;
                List<List<Rectangle>> listSplitGrayBlockPostion = ucGraphicSplit1.SplitGraphicBytes();
                List<List<byte[,]>> listBlockBytes = GetListBlockBytes(availableBytes, listSplitGrayBlockPostion);
                List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList = GetInspectionEigenvalueDetailList(listBlockBytes);
                StatisticsEigenValues(listBlockBytes, inspectionEigenvalueDetailList);
                MergeInspectionEigenvalueDetailListAndListSplitGrayBlockPostion(inspectionEigenvalueDetailList, listSplitGrayBlockPostion);
                SaveListBlockBytes(inspectionEigenvalueDetailList);
                MessageBox.Show("样本学习成功！");
            }
            catch (Exception ex)
            {
                MessageBox.Show("发生错误：" + ex.Message);
            }
            finally
            {
                btnSampleLearning.Enabled = true;
            }
        }

        /// <summary>
        /// 将inspectionEigenvalueDetailList和位置信息合并
        /// </summary>
        /// <param name="inspectionEigenvalueDetailList"></param>
        /// <param name="listSplitGrayBlockPostion"></param>
        private void MergeInspectionEigenvalueDetailListAndListSplitGrayBlockPostion(List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList, List<List<Rectangle>> listSplitGrayBlockPostion)
        {
            foreach (InspectionEigenvalueDetail inspectionEigenvalueDetail in inspectionEigenvalueDetailList)
            {
                Rectangle rectangle = listSplitGrayBlockPostion[inspectionEigenvalueDetail.Layer][inspectionEigenvalueDetail.Piece];
                inspectionEigenvalueDetail.Area = rectangle.Left + "," + rectangle.Top + "," + rectangle.Width + "," + rectangle.Height;
            }
        }

        /// <summary>
        /// 将忽略区域的点设为0
        /// </summary>
        /// <param name="grayBytes"></param>
        /// <param name="dtInspectionIgnore"></param>
        private void IgnoreBytes(byte[,] grayBytes, DataTable dtInspectionIgnore)
        {
            foreach (DataRow dr in dtInspectionIgnore.Rows)
            {
                Rectangle rectangle = new Rectangle(Convert.ToInt32(dr["left"]), Convert.ToInt32(dr["top"]),
                        Convert.ToInt32(dr["width"]), Convert.ToInt32(dr["height"]));
                for (int i = rectangle.Left; i < rectangle.Left + rectangle.Width; i++)
                {
                    for (int j = rectangle.Top; j < rectangle.Top + rectangle.Height; j++)
                    {
                        grayBytes[i, j] = 0;
                    }
                }
            }
        }

        /// <summary>
        /// 分段统计每层每块的灰度值个数
        /// </summary>
        /// <param name="listBlockBytes"></param>
        private void StatisticsEigenValues(List<List<byte[,]>> listBlockBytes, List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList)
        {
            int size = 32;
            int blockSize = 8;
            for (int i = 0; i < listBlockBytes.Count; i++)
            {
                List<byte[,]> blockBytes = listBlockBytes[i];
                for (int j = 0; j < blockBytes.Count; j++)
                {
                    byte[,] bytes = blockBytes[j];
                    int[] eigenValues = new int[size];
                    foreach (byte b in bytes)
                    {
                        eigenValues[b / blockSize]++;
                    }
                    var inspectionEigenvalueDetail = (from item in inspectionEigenvalueDetailList
                                                      where item.Layer == i && item.Piece == j
                                                      select item).First();
                    inspectionEigenvalueDetail.EigenValues = eigenValues;
                }
            }
            
        }

        /// <summary>
        /// 保存
        /// </summary>
        private void SaveListBlockBytes(List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList)
        {
            string sql = BuildInsertSql(inspectionEigenvalueDetailList);
            DBUtils.ExecuteSql(sql);
        }

        /// <summary>
        /// 构建插入SQL
        /// </summary>
        /// <param name="inspectionEigenvalueDetailList"></param>
        /// <returns></returns>
        private string BuildInsertSql(List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList)
        {
            StringBuilder sb = new StringBuilder();
            //先把数据库里的数据清空再插入
            sb.Append("delete from InspectionEigenvalueDetail where PID=" + Pid + "\n");
            foreach (InspectionEigenvalueDetail inspectionEigenvalueDetail in inspectionEigenvalueDetailList)
            {
                sb.Append("insert into InspectionEigenvalueDetail(PID, Layer, Piece, Area, Variance, EigenValue)");

                sb.Append(" values(" + Pid + ", " + inspectionEigenvalueDetail.Layer + ", " + inspectionEigenvalueDetail.Piece
                    + ", '" + inspectionEigenvalueDetail.Area + "', " + inspectionEigenvalueDetail.Variance
                    + ", '" + inspectionEigenvalueDetail.GetEigenValuesWithComma() + "')\n");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 得到每一层每一块的方差list
        /// </summary>
        /// <param name="listBlockBytes"></param>
        /// <returns></returns>
        private List<InspectionEigenvalueDetail> GetInspectionEigenvalueDetailList(List<List<byte[,]>> listBlockBytes)
        {
            List<InspectionEigenvalueDetail> inspectionEigenvalueDetailList = new List<InspectionEigenvalueDetail>();
            for (int i = 0; i < listBlockBytes.Count; i++)
            {
                List<byte[,]> blockBytes = listBlockBytes[i];
                for (int j = 0; j < blockBytes.Count; j++)
                {
                    byte[,] bytes = blockBytes[j];
                    inspectionEigenvalueDetailList.Add(new InspectionEigenvalueDetail(i, j, "", GetVariance(bytes)));
                }
            }
            return inspectionEigenvalueDetailList;
        }

        /// <summary>
        /// 计算方差
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private double GetVariance(byte[,] bytes)
        {
            double variance = 0;
            double pixelCountX = 1;
            double pixelCountY = 1;
            double pixelCount = 1;
            double grayAverage = 0;
            double graySum = 0;
            try
            {
                pixelCountX = bytes.GetLength(0);
                pixelCountY = bytes.GetLength(1);
                pixelCount = (pixelCountX * pixelCountY);
                for (int j = 0; j < pixelCountY; j++)
                {
                    for (int i = 0; i < pixelCountX; i++)
                    {
                        int gray = bytes[i, j];
                        graySum += gray;
                    }
                }
                grayAverage = graySum / pixelCount;
                double diffValueSum = 0;
                for (int j = 0; j < pixelCountY; j++)
                {
                    for (int i = 0; i < pixelCountX; i++)
                    {
                        diffValueSum += Math.Pow(bytes[i, j] - grayAverage, 2);
                    }
                }
                variance = diffValueSum / pixelCount;
                variance = (int)Math.Pow(variance, 0.5);
                return variance;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return Double.MaxValue;
            }
        }

        /// <summary>
        /// 得到每块的bytes
        /// </summary>
        /// <param name="availableBytes"></param>
        /// <param name="listSplitGrayBlockPostion"></param>
        /// <returns></returns>
        private static List<List<byte[,]>> GetListBlockBytes(byte[,] availableBytes, List<List<Rectangle>> listSplitGrayBlockPostion)
        {
            List<List<byte[,]>> listBlockBytes = new List<List<byte[,]>>();
            foreach (List<Rectangle> rectangles in listSplitGrayBlockPostion)
            {
                List<byte[,]> listBytes = new List<byte[,]>();
                foreach (Rectangle rectangle in rectangles)
                {
                    byte[,] bytes = new byte[rectangle.Width, rectangle.Height];
                    for (int k = rectangle.Top, n = 0; k < rectangle.Top + rectangle.Height; k++, n++)
                    {
                        for (int i = rectangle.Left, j = 0; i < rectangle.Left + rectangle.Width; i++, j++)
                        {
                            bytes[j, n] = availableBytes[i, k];
                        }
                    }
                    listBytes.Add(bytes);
                }
                listBlockBytes.Add(listBytes);
            }
            return listBlockBytes;
        }

        /// <summary>
        /// 得到有效区域2D bytes
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        private static byte[,] GetAvailableBytes2D(byte[,] grayBytes, Rectangle rectangle)
        {
            byte[,] availableBytes = new byte[rectangle.Width, rectangle.Height];
            for (int k = rectangle.Top, n = 0; k < rectangle.Top + rectangle.Height; k++, n++)
            {
                for (int i = rectangle.Left, j = 0; i < rectangle.Left + rectangle.Width; i++, j++)
                {
                    availableBytes[j, n] = grayBytes[i, k];
                }
            }
            return availableBytes;
        }

        /// <summary>
        /// GetGrayBytes2D:下标0代表x轴坐标，1代表y轴坐标。默认提取绿色通道像素值。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        private static byte[,] GetGrayBytes2D(Bitmap bitmap)
        {
            if (bitmap == null) return null;
            string strPixelFormat = bitmap.PixelFormat.ToString();
            if (strPixelFormat == "Format8bppIndexed")
            {
                return ConvertGrayBytesToDoubleArray(GetGrayBytesFor8bit(bitmap), bitmap.Width, bitmap.Height);
            }
            else
            {
                return ConvertGrayBytesToDoubleArray(GetGrayBytesFor32bit(bitmap, 2), bitmap.Width, bitmap.Height);
            }
        }
        private static byte[,] ConvertGrayBytesToDoubleArray(byte[] grayBytes, int imageWidth, int imageHeight)
        {
            byte[,] processedGrayBytes = new byte[imageWidth, imageHeight];
            int k = 0;
            for (int j = 0; j < imageHeight; j++)
            {
                for (int i = 0; i < imageWidth; i++)
                {
                    processedGrayBytes[i, j] = grayBytes[k++];
                }
            }
            return processedGrayBytes;
        }
        private static byte[] GetGrayBytesFor8bit(Bitmap bitmap)
        {
            byte[] grayValues = null;
            try
            {
                if (bitmap != null)
                {
                    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                    System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
                    IntPtr ptr = bmpData.Scan0;
                    int bytes = bitmap.Width * bitmap.Height;
                    grayValues = new byte[bytes];
                    System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
                    bitmap.UnlockBits(bmpData);
                }
                return grayValues;
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("GetGrayBytesFor8bit:{0}", ex.Message));
                return null;
            }
        }
        private static byte[] GetGrayBytesFor32bit(Bitmap bitmap, int intMethod)
        {
            byte[] rgbValues = null;
            byte[] grayValues = null;
            if (bitmap != null)
            {
                double r = 0, g = 0, b = 0;
                switch (intMethod)
                {
                    case 0:
                        {
                            r = 0.299;
                            g = 0.587;
                            b = 0.114;
                            break;
                        }
                    case 1:
                        {
                            r = 1;
                            g = 0;
                            b = 0;
                            break;
                        }
                    case 2:
                        {
                            r = 0;
                            g = 1;
                            b = 0;
                            break;
                        }
                    case 3:
                        {
                            r = 0;
                            g = 0;
                            b = 1;
                            break;
                        }
                    default:
                        r = 0;
                        g = 1;
                        b = 0;
                        break;
                }
                try
                {
                    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                    System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
                    IntPtr ptr = bmpData.Scan0;
                    int bytes = bitmap.Width * bitmap.Height;
                    int rgbBytes = bytes * 3;
                    rgbValues = new byte[rgbBytes];
                    grayValues = new byte[bytes];
                    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, rgbBytes);
                    int j = 0;
                    for (int i = 0; i < rgbBytes; i += 3)
                    {
                        double colorTemp = rgbValues[i + 2] * r + rgbValues[i + 1] * g + rgbValues[i] * b;
                        grayValues[j++] = (byte)colorTemp;
                    }
                    bitmap.UnlockBits(bmpData);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("GetGrayBytesFor32bit:{0}", ex.Message));
                }
            }
            return grayValues;
        }
    }

    class InspectionEigenvalueDetail
    {
        private int layer;
        public int Layer
        {
            get { return layer; }
            set { layer = value; }
        }

        private int piece;
        public int Piece
        {
            get { return piece; }
            set { piece = value; }
        }

        private string area;
        public string Area
        {
            get { return area; }
            set { area = value; }
        }

        private double variance;
        public double Variance
        {
            get { return variance; }
            set { variance = value; }
        }

        private int[] eigenValues;
        public int[] EigenValues
        {
            get { return eigenValues; }
            set { eigenValues = value; }
        }

        /// <summary>
        /// 得到64段的灰度值个数，以“,”号分隔
        /// </summary>
        /// <returns></returns>
        public string GetEigenValuesWithComma()
        {
            StringBuilder sb = new StringBuilder();
            foreach (int eigenValue in EigenValues)
            {
                sb.Append(eigenValue + ",");
            }
            sb.Remove(sb.Length - 1, 1);   //移除最后一个逗号
            return sb.ToString();
        }

        /// <summary>
        /// 以“,”号分隔的灰度值设置
        /// </summary>
        /// <returns></returns>
        public void SetEigenValuesWithComma(string eigenValuesWithComma)
        {
            string[] eigenValueStrs = eigenValuesWithComma.Split(',');
            this.eigenValues = new int[eigenValueStrs.Length];
            for (int i = 0; i < eigenValueStrs.Length; i++)
            {
                eigenValues[i] = Convert.ToInt32(eigenValueStrs[i]);
            }
        }

        public InspectionEigenvalueDetail(int layer, int piece, string area, double variance)
        {
            this.layer = layer;
            this.piece = piece;
            this.area = area;
            this.variance = variance;
        }

    }
}
